#==================================================
#	Componse Input - 组合键输入
#==================================================
# * 延迟判断按键是否被按下，防止操作过于快速导致组合键失效
# * 例，按下：小键盘方向键左+空格，则触发操作：
#    add_mapper("刺", 
#    	["ui_accept", "ui_left", ["ui_accept"]
#    )
#==================================================
# @datetime: 2021-12-21 22:50:05
#==================================================

extends "Event.gd"


# 每个按键组合会在其中一个按键被触发时进行计时器倒计时
# 进行组合件判定，如果到达时间，则会进行释放掉所有已按
# 下的记录，会在此进行发出释放掉的按键，以便可以连接这
# 个信号去执行其他可能的操作 
## @keys  释放掉的key
## @release_all  是否全部释放掉了
## @map_name  释放掉的是哪个组合键
signal released(keys, release_all, map_name)


## 按键映射
var input_mapper_dict : Dictionary = {}
var input_mapper_list : Array = []


#==================================================
#   自定义方法
#==================================================
#(override)
func _init_data():
	register_to_function("ComponseInput")


#(override)
func _process_input(_delta):
	._process_input(_delta)
	for m in input_mapper_list:
		m.physics_process()


##  添加 key 映射
## @key  判断的 key
## @input_maps  触发的键盘映射
## @just  仅仅在按下那一刻才检测到
## @or_input  其中的一个按下即为各个都按下
func add_mapper(
	key: String, 
	input_maps: PoolStringArray, 
	just: PoolStringArray = [],
	or_input: PoolStringArray = []
):
	var ck = ComponseKey.new(
		self, key, input_maps, just, or_input
	)
	input_mapper_list.push_back(ck)
	input_mapper_dict[key] = ck


## 是否全部输入了
func is_inputted(key: String) -> bool:
	if input_mapper_dict.has(key):
		return input_mapper_dict[key].is_inputted()
	printerr(
		host.name, ": ", 
		self, ".input_mapper_dict 中不存在这个映射按键组合！"
	)
	return false


##  获取已按下的键
## @key  
func get_inputed_key(key: String) -> Array:
	return input_mapper_dict[key].get_inputed()


##  清除按键记录 
## @key  
func clear(key: String):
	input_mapper_dict[key].queue_free()
	input_mapper_dict[key].clear()
	input_mapper_dict.erase(key)


##  清空所有按键记录
func clear_all():
	for key in input_mapper_dict:
		input_mapper_dict[key].clear()


##  回调方法 
##（每个组合键会在释放掉记录的按键时，调用这个方法）
## @data  
func callback(data):
	emit_signal("released", 
		data.released_keys, 
		data.released_all, 
		data.map_name
	)



#==================================================
#   组合输入
#==================================================
class ComponseKey:
	
	const DURATION = 0.1
	
	var _host : Node
	var _map_name : String = ""
	var _release_timer := Timer.new()
	var _delta = 1.0 / ProjectSettings.get("physics/common/physics_fps")
	# 仅仅在按下的时候才被检测
	var _just := {}
	# 或输入（其中一个按下了，则全部都算作按下了）
	var _or_input := {}
	
	# 已按下的按键
	var _inputted := {}
	# 已按下按键的数量
	var _inputted_count : int = 0
	
	
	func _init(
		host: Node 	# 这个对象的宿主，用于添加计时器到这个节点上
		, map_name : String
		, input_maps: PoolStringArray 	# 判定的按键 
		, just: PoolStringArray = []	# 是否只是在按下那一刻才测到
		, or_input: PoolStringArray = []	# 其中一个按下，即为都按下
	) -> void:
		_host = host
		_map_name = map_name
		# 添加重置计时器
		_release_timer.wait_time = DURATION
		_release_timer.one_shot = true
		_release_timer.autostart = false
		if _release_timer.connect("timeout", self, "release") != OK:
			printerr(self, "连接信号时出现错误")
		_host.add_child(_release_timer)
		# 初始化数据
		for key in input_maps:
			_inputted[key] = false
		for key in just:
			_just[key] = key
		for key in or_input:
			_or_input[key] = key
	
	
	## 检测
	func physics_process() -> void:
		for key in _inputted.keys():
			# 按下时捕获输入
			if _just.has(key):
				if Input.is_action_just_pressed(key):
					set_inputted_key(key)
			# 每帧都捕获输入
			else:
				if  Input.is_action_pressed(key):
					set_inputted_key(key)
	
	
	## 设置已输入的按键
	func set_inputted_key(key: String):
		if key in _or_input:
			for i in _or_input:
				if !_inputted[i]:
					_inputted[i] = true
					_inputted_count += 1
		else:
			if !_inputted[key]:
				_inputted[key] = true
				_inputted_count += 1
		# 开始倒计时
		if _release_timer.is_stopped():
			_release_timer.start()
		# 如果已按下全部按键，则进行释放
		elif is_all_inputted():
			_release_timer.stop()
			release()
	
	
	## 是否全部按下
	func is_all_inputted() -> bool:
		return _inputted_count == _inputted.size()
	
	
	## 释放掉所有按键记录，重置状态
	func release() -> void:
		var list = get_inputed()
		clear()
		# 回调宿主的 callback 方法
		_host.callback({
			released_keys = list, 
			released_all = (list.size() == _inputted.size()), 
			map_name = _map_name,
		})
	
	
	## 获取已按下的键
	func get_inputed() -> Array:
		var list := []
		for key in _inputted.keys():
			if _inputted[key]:
				list.push_back(key)
		return list
	
	
	## 清除内容
	func clear():
		_release_timer.stop()
		_inputted_count = 0
		# 重置按下状态
		for key in _inputted.keys():
			_inputted[key] = false
	
	## 清除
	func queue_free():
		_release_timer.queue_free()
	

